package org.jflame.auth.web.spring;

import org.jflame.auth.LoginUser;
import org.jflame.auth.context.UserContextHolder;
import org.jflame.commons.model.CallResult;
import org.jflame.commons.model.CallResult.ResultEnum;
import org.jflame.commons.util.UrlHelper;
import org.jflame.web.WebUtils;
import org.jflame.web.spring.SpringWebUtils;

import java.io.IOException;
import java.util.Optional;
import java.util.function.Function;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

/**
 * 登录拦截器
 * 
 * @author charles.zhang
 */
public class LoginInterceptor implements HandlerInterceptor {

    protected final Logger logger = LoggerFactory.getLogger(LoginInterceptor.class);

    private String loginUrl;
    private boolean ignoreWebResource = false;
    private final CallResult<?> failed = new CallResult<>(ResultEnum.NO_AUTH);

    private Function<HttpServletRequest,LoginUser> authenticator = (req) -> {
        Optional<LoginUser> user = UserContextHolder.getLoginUser();
        if (user.isPresent()) {
            return user.get();
        }
        return null;
    };

    @Override
    public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler) throws Exception {
        if (isIgnoreUrl(req)) {
            return true;
        }
        LoginUser loginUser = null;
        try {
            loginUser = authenticator.apply(req);
        } catch (Exception e) {
            logger.error("身份认证异常", e.getMessage());
        }
        if (loginUser == null) {
            onFailed(req, resp, handler);
            return false;
        } else {
            onSuccess(loginUser, req, resp);
        }

        return true;
    }

    /**
     * 身份认证失败处理
     * 
     * @param req
     * @param resp
     * @param handler
     * @throws IOException
     * @throws ServletException
     */
    private void onFailed(HttpServletRequest req, HttpServletResponse resp, Object handler)
            throws IOException, ServletException {
        if (SpringWebUtils.isJsonResult(req, handler)) {
            resp.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            WebUtils.outJson(resp, failed);
        } else {
            if (null != failed.getMessage()) {
                req.setAttribute("errorMsg", failed.getMessage());
            }
            if (loginUrl != null) {
                if (UrlHelper.isAbsoluteUri(loginUrl)) {
                    resp.sendRedirect(loginUrl);
                } else {
                    req.getRequestDispatcher(loginUrl)
                            .forward(req, resp);
                }
            } else {
                resp.sendError(HttpServletResponse.SC_UNAUTHORIZED, "401 unauthorized");
            }
        }
    }

    private boolean isIgnoreUrl(HttpServletRequest request) {
        return isIgnoreWebResource() && WebUtils.isWebResource(request);
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {

    }

    /**
     * 验证失败登录页地址
     * 
     * @param loginUrl
     */
    public void setLoginUrl(String loginUrl) {
        this.loginUrl = loginUrl;
    }

    public String getLoginUrl() {
        return loginUrl;
    }

    /**
     * 设置身份认证方法
     * 
     * @param authenticator
     * @return
     */
    public LoginInterceptor authc(Function<HttpServletRequest,LoginUser> authenticator) {
        if (authenticator != null) {
            this.authenticator = authenticator;
        }
        return this;
    }

    /**
     * 忽略web静态资源拦截
     * 
     * @return
     */
    public LoginInterceptor ignoreWebResource() {
        this.ignoreWebResource = true;
        return this;
    }

    public boolean isIgnoreWebResource() {
        return ignoreWebResource;
    }

    public void setIgnoreWebResource(boolean ignoreWebResource) {
        this.ignoreWebResource = ignoreWebResource;
    }

    /**
     * 登录验证成功后执行操作
     * 
     * @param loginUser 当前登录用户
     * @param req HttpServletRequest
     * @param resp HttpServletResponse
     */
    protected void onSuccess(LoginUser loginUser, HttpServletRequest req, HttpServletResponse resp) {

    }

}
